/*************************************************************************
 *                                                                       *
 * This file is part of the FreeMAPs distribution.  For full copyright   *
 * information, see COPYRIGHT and COPYING.LESSER.                        *
 *                                                                       *
 * Author:           sqbang                                              *
 * Email:            sqbang@ustc.edu                                     *
 * Date:             2013.11.19                                          *
 *                                                                       *
 *************************************************************************/
#if 0
//declaration header
#include "COMMON/mesh/FluentIO.h"

//C++ headers
#include <fstream>

//COMMON headers
#include "COMMON/info/InfoManager.h"
#include "COMMON/info/CommonInfo.h"
#include "COMMON/mesh/FluentMesh.h"
#include "COMMON/mesh/MeshGroup.h"
#include "COMMON/mesh/MeshTools.h"
#include "COMMON/mesh/UnstructuredMesh.h"
#include "COMMON/tbox/Utilities.h"

namespace
{

bool
is_boundary (
    const std::string& region_type)
{
    if (region_type == "fluid" || region_type == "solid" || region_type == "interior")
    {
        return false;
    }
    else
    {
        return true;
    }
}

}


namespace FreeMAPs
{

FluentIO::FluentIO()
{}



FluentIO::~FluentIO()
{}



void
FluentIO::read (
    const std::string& file_name,
    std::map<int, libMesh::Point>& nodes,
    std::vector<Elem>& elems,
    MeshGroup& mesh_group) const
{
    FluentMesh fluent_reader (file_name);
    const int ierr = fluent_reader.read();
    FREEMAPS_CHKERR (ierr);

    /*
     * process regions.
     * note that this should be done before processing cells.
     * so when dealing cells, one known which region or boundary it belongs to.
     */
    const std::string default_region_name = "default_interior";
    const std::string default_boundary_name = "default_boundary";

    std::map<int, std::string> fluent_region_to_mesh_region;
    fluent_region_to_mesh_region.insert (std::make_pair (-1, default_region_name));
    ElemGroup elem_group (default_region_name);
    mesh_group.insert_elem_group (elem_group);

    fluent_region_to_mesh_region.insert (std::make_pair (-2, default_boundary_name));
    NodeGroup node_group (default_boundary_name);
    mesh_group.insert_node_group (node_group);
    //std::vector<std::string> regions;

    const std::map<int, FluentMesh::Region_t>& mesh_regions = fluent_reader.get_mesh_regions();
    for (std::map<int, FluentMesh::Region_t>::const_iterator region_it = mesh_regions.begin();
            region_it != mesh_regions.end(); ++region_it)
    {
        const FluentMesh::Region_t& fregion = region_it->second;

        //std::cout << "\nregion type = " << fregion.type << std::endl;
        //std::cout << "region name = " << fregion.name << std::endl;
        //std::cout << "region index = " << fregion.index << std::endl;
        if (is_boundary (fregion.type)) //it is a boundary. and build it into a node_set.
        {
            std::string name = fregion.name;
            NodeGroup node_group (name);
            mesh_group.insert_node_group (node_group);

            fluent_region_to_mesh_region [fregion.index] = name;
        }
        else
        {
            std::string name = fregion.name;
            //std::cout << "region name = " << name << std::endl;

            //std::string material;
            //Tokenizer tokenizer(name,"_");
            //std::vector<std::string> property = tokenizer.split();
            //if (property.size() >= 2)
            //    material = Material::FormatMaterialString(property[1]);

            //regions.push_back (name);
            //mesh.set_subdomain_label (r, name);
            //mesh.set_subdomain_material(r, material);
            ElemGroup elem_group (name);
            mesh_group.insert_elem_group (elem_group);

            fluent_region_to_mesh_region [fregion.index] = name;
        }
    }

    /*
     * process nodes
     */
    const std::vector<FluentMesh::Node_t> & mesh_nodes = fluent_reader.get_mesh_nodes();
    for (size_t n = 0; n < mesh_nodes.size(); n++)
    {
        const FluentMesh::Node_t& fnode = mesh_nodes[n];
        nodes.insert (std::make_pair (fnode.index - 1, libMesh::Point(fnode.x, fnode.y, fnode.z)));
        //std::cout << "node " << n << ", index = " << fnode.index << ", belongs to region " << fnode.region << " named " << fluent_region_to_mesh_region [fnode.region] << std::endl;
        //std::cout << "node " << n << ", index = " << fnode.index << ", " << fnode.x << ", " << fnode.y << ", " << fnode.z << std::endl;
    }

    if (nodes.empty ())
    {
        FREEMAPS_ERROR ("ERRRO, no node has read from file \"" << file_name << "\", make sure it is a FLUENT type mesh file");
    }
    //std::cout << "num nodes = " << mesh_nodes.size() << std::endl;

    /*
     * process cells
     */
    const std::vector<FluentMesh::Cell_t>& mesh_cells = fluent_reader.get_mesh_cells();
    elems.resize (mesh_cells.size());
    //std::cout << "num cells = " << mesh_cells.size() << std::endl;
    for (size_t c = 0; c < mesh_cells.size(); ++c)
    {
        const FluentMesh::Cell_t& cell = mesh_cells [c];
#if 0
        //Elem* elem = 0;

        switch(cell.type)
        {
        case 1: elem = mesh.add_elem (Elem::build     (TRI3).release()); break;
        case 2: elem = mesh.add_elem (Elem::build     (TET4).release()); break;
        case 3: elem = mesh.add_elem (Elem::build    (QUAD4).release()); break;
        case 4: elem = mesh.add_elem (Elem::build     (HEX8).release()); break;
        case 5: elem = mesh.add_elem (Elem::build (PYRAMID5).release()); break;
        case 6: elem = mesh.add_elem (Elem::build   (PRISM6).release()); break;
        default: break;
        }

        for (size_t i = 0; i < cell.nodes.size(); i++)
        {
            elem->set_node(i) = mesh.node_ptr( cell.nodes[i]-1 );
        }

        // which region this tet belongs to
        elem->subdomain_id() = fluent_region_to_mesh_region[cell.region];
#endif
        std::string subdomain_name = default_region_name;
        std::map<int, std::string>::const_iterator find_region = fluent_region_to_mesh_region.find (cell.region);
        if (find_region != fluent_region_to_mesh_region.end())
        {
            subdomain_name = find_region->second;
        }
        //std::cout << "cell " << c << ", region = " << cell.region << "\tbelongs to " << subdomain_name << std::endl;
        mesh_group.insert_elem_to_elem_group (subdomain_name, c);

        const int elem_type_id = cell.type;
        const int n_nodes_in_elem = cell.nodes.size();
        const ElemType elem_type = MeshTools::get_elem_type (MeshFileFormat::FLUENT, elem_type_id, n_nodes_in_elem);
        std::vector<int> node_ids = cell.nodes;
        for (std::size_t pn = 0; pn < node_ids.size(); ++pn)
        {
            --node_ids[pn];  //fluent mesh number from 1, not 0.
        }
        MeshTools::reorder_nodes_in_elem (MeshFileFormat::FLUENT, elem_type, node_ids);
        elems[cell.index - 1].rebuild(
            cell.index,
            elem_type,
            0,
            FREEMAPS_INVALID_STRING_VALUE,
            node_ids);
    }

    //mesh.set_n_subdomains() = fluent_region_to_mesh_region.size();
    //FREEMAPS_NOT_FINISHED();

    /*
     * build boundary face set for later usage
     */
    //std::set<FluentMesh::Face_t, FluentMesh::lt_face> face_table;
    const std::vector<FluentMesh::Face_t>& mesh_faces = fluent_reader.get_mesh_faces();
    //std::cout << "num faces = " << mesh_faces.size() << std::endl;
    for (size_t f = 0; f < mesh_faces.size(); f++)
    {
        const FluentMesh::Face_t& face =  mesh_faces[f];
        //std::cout << "face " << f << ", index = " << face.index << ", type = " << face.type << ", nodes = " << Utilities::get_rearranged_vector (face.nodes, false, 10) << std::endl;
        //if(face.cell_right != face.cell_left)
        //{
        //face_table.insert (face);
        //}
        const int region_id = face.region;
        std::map<int, FluentMesh::Region_t>::const_iterator find_region = mesh_regions.find (region_id);
        if (find_region != mesh_regions.end())
        {
            const FluentMesh::Region_t& fregion = find_region->second;
            if (is_boundary (fregion.type))
            {
                for (std::size_t pos = 0; pos < face.nodes.size(); ++pos)
                {
                    mesh_group.insert_node_to_node_group (fregion.name, face.nodes [pos] - 1);
                    //std::cout << "name = " << fregion.name << ", node = " << face.nodes [pos] -1 << std::endl;
                }
            }
        }
        else
        {
            //for (std::size_t pos = 0; pos < face.nodes.size(); ++pos)
            //{
            //mesh_group.insert_node_to_node_group (fregion.name, face.nodes [pos] - 1);
            //}
        }
    }

    /*
     * build neighbor information for boundary element. then elem->neighbor() is functional
     */
    //mesh.find_neighbors();

    /*
     * generate all the bounrary side with id -1
     */
    //mesh.generate_boundary_info(-1);
#if 0
    std::map<std::string, std::pair<short int, bool> > bd_map;
    typedef std::map<std::string, std::pair<short int, bool> >::iterator Bd_It;

    //classfy boundary label
    //std::vector<unsigned int>       elems;
    //std::vector<unsigned short int> sides;
    //std::vector<short int>          bds;

    // get all the boundary element
    //mesh.boundary_info->build_side_list (elems, sides, bds);
    for (size_t nbd = 0; nbd < elems.size(); nbd++ )
    {
        // get the element which has boundary/interface side
        const Elem* elem = mesh.elem(elems[nbd]);
        short int bd_index = bds[nbd];

        // face already has label (bd_id = -1 for labelless boundary side)
        //if(bd_index >= 0 )  continue;


        //is it an interface side
        /*
        if( elem->neighbor(sides[nbd])!=NULL )
        {
            // the element and its neighbor should in diffetent subdomain
            unsigned int sbd_id1 = elem->subdomain_id();
            unsigned int sbd_id2 = elem->neighbor(sides[nbd])->subdomain_id();

            // delete the overkilled boundary side
            if (sbd_id1 == sbd_id2)
            {
                mesh.boundary_info->remove(elem, sides[nbd]);
                continue;
            }

            // the side should be an interface side
            genius_assert(elem->on_interface());
            genius_assert(elem->neighbor(sides[nbd])->on_interface());

            //remove the pair-element from boundary
            mesh.boundary_info->remove(elem, sides[nbd]);
            mesh.boundary_info->remove(elem->neighbor(sides[nbd]),
                                       elem->neighbor(sides[nbd])->which_neighbor_am_i(elem));

            // build the label for the interface, which has the form of RegionLabel1_to_RegionLabel2,
            // the two region is alpha ordered.
            std::string bd_label;
            std::string region1 = regions[sbd_id1];
            std::string region2 = regions[sbd_id2];
            if( region1 < region2)
                bd_label = region1 + "_to_" + region2;
            else
                bd_label = region2 + "_to_" + region1;


            // if the label already exist
            if( bd_map.find(bd_label) != bd_map.end() )
                bd_index = bd_map.find(bd_label)->second.first;
            else
            {
                //else, increase bd_index, insert it into bd_map
                bd_index = bd_map.size() + 1024;
                bd_map[bd_label] = std::make_pair(bd_index, false);
            }

            // add pair-element to boundary with new bd_index
            mesh.boundary_info->add_side(elem, sides[nbd], bd_index);
            mesh.boundary_info->add_side(elem->neighbor(sides[nbd]),
                                         elem->neighbor(sides[nbd])->which_neighbor_am_i(elem), bd_index);
        }*/
        // a boundary side
        //else
        {
            unsigned int sbd_id = elem->subdomain_id();
            mesh.boundary_info->remove(elem, sides[nbd]);
            std::string bd_label = regions[sbd_id] + "_Neumann";

            // if the label already exist
            if( bd_map.find(bd_label) != bd_map.end() )
                bd_index = bd_map.find(bd_label)->second.first;
            else
            {
                //else, increase bd_index, insert it into bd_map
                bd_index = bd_map.size() + 1024;
                bd_map[bd_label] = std::make_pair(bd_index, false);
            }

            // add pair-element to boundary with new bd_index
            mesh.boundary_info->add_side(elem, sides[nbd], bd_index);
        }

    }

    /*
     * after the previous remove and insert operation, the number of boundary id maybe changed. renumber it here.
     */
    //mesh.boundary_info->rebuild_ids();
#endif

    /*
     * write down bd labels
     */
    //Bd_It bd_it = bd_map.begin();
    //for(; bd_it != bd_map.end(); ++bd_it)
    //{
    //mesh.boundary_info->set_label_to_id( bd_it->second.first, bd_it->first, bd_it->second.second);
    //}

    /*
     * now remove empty group
     */
    mesh_group.remove_empty_group();
    //std::cout << "mesh_group = " << mesh_group << std::endl;
    //FREEMAPS_NOT_FINISHED();
}



void
FluentIO::write (
    const std::string& file_name) const
{
    (void)file_name;
    FREEMAPS_NOT_FINISHED();
}

}
#endif//
